package ce.com.cenewbluesdk.proxy;

import android.os.Handler;

import java.util.PriorityQueue;

import ce.com.cenewbluesdk.CEBC;
import ce.com.cenewbluesdk.uitl.Lg;
import ce.com.cenewbluesdk.bluetooth.CEBlueToothBase;
import ce.com.cenewbluesdk.entity.CEDevData;
import ce.com.cenewbluesdk.entity.QueueSendData;
import ce.com.cenewbluesdk.queue.CEProtocolBase;

/**
 * Created by Administrator on 2016/11/7 0007.
 */
public class CEDevQueue implements CEBlueToothBase.blueToothReceiptDataListen {


//    private int N;

    /**
     * 发送失败的重新发送次数
     */
    private static final int maxSendFailTime = 0;
    /**
     * 数据发送超时的时间
     */
    private static final int timeOutTime = 8000;


    private int byteSendFailTimes = 0;
    private int dataSendFailTimes = 0;

    private byte[] currSendData;

    /**
     * 当前蓝牙发送数据的状态
     * 0 蓝牙是空闲的 没有发送数据
     * 1 蓝牙正在发送数据
     * 2 蓝牙数据发送完成，等待设备侧会ACk
     */
    private static final int sendFree = 0;
    private static final int sending = 1;
    private static final int sendwaitAck = 2;

    private int sendState = 0;

    /**
     * 蓝牙类型
     */
    private CEBlueToothBase bluetooth;
    /**
     * 蓝牙协议
     */
    private CEProtocolBase protocol;
    private CEBluetoothProxyBase proxyBase;

    public CEDevQueue(CEBluetoothProxyBase proxyBase, CEProtocolBase protocol, CEBlueToothBase bluetooth) {
        this.bluetooth = bluetooth;
        this.protocol = protocol;
        this.proxyBase = proxyBase;
        bluetooth.setBlueReceiptDataListen(this);
    }

    public void setBluetooth(CEBlueToothBase bluetooth) {

        this.bluetooth = bluetooth;
    }

    public void setProtocol(CEProtocolBase protocol) {
        this.protocol = protocol;
    }

    protected PriorityQueue<CEDevData> sendQueue = new PriorityQueue<CEDevData>();

    public void push(CEDevData devData) {
//        devData.setN(N);
//        addN();
        sendQueue.add(devData);
        Lg.e("push size " + sendQueue.size() + " sendState:" + sendState);
        if (sendQueue.size() == 1) {
            sendDevData(devData);
        }

    }

    public void remove(CEDevData devData) {
        sendQueue.remove(devData);
    }

    public void clearDate() {

        sendQueue.clear();
    }

    public CEDevData poll() {

        return sendQueue.poll();
    }

    public CEDevData peek() {

        return sendQueue.peek();
    }

    @Override
    public void blueToothReceiptData(byte[] bytes) {
        /**
         * 这个需要协议解析数据
         */
        CEDevData tDevData = protocol.Analysis(bytes);
        if (tDevData != null) { // 不为null,即接收数据完成
            Lg.e("blueToothReceiptData " + tDevData.getCmd() + " " + tDevData);
            if (tDevData.getCmd() == CEBC.AckApp.CMD_ACK) {
                //TODO 数据发送完成,接收到设备侧ack 出队列
                int ackValue = tDevData.getData()[0];
                if (sendState != sendwaitAck) {
                    Lg.e("当前不是正在等待回复ACK，但是接收到了Ack");
                    return;
                }
                CEDevData sendOverData = peek();
                if (sendOverData.getDataType() == tDevData.getDataType()) {
                    setSendState(sendFree);
                    sendTimeoutH.removeCallbacks(waitAckSendTimeoutR); // 不管成功和失败都需要结束ACK超时的Run
                    if (ackValue == CEBC.AckApp.ACK_TYPE_SUCCESS) {
                        Lg.e("poll push size " + sendQueue.size());
                        dataSendFailTimes = 0;
                        proxyBase.dataSendSucceed(sendOverData);
                        poll();// 将发送成功的数据弹出队列
                        CEDevData topDevData = peek();
                        sendDevData(topDevData);
                    } else {
                        ackEreOrWaitTimeOut();
                    }
                } else {
                    Lg.e(" 蓝牙发送完了后设备会的ACK 与设备顶部的 数据不对 ");
                    // 这里不需要进行处理，因为需要继续等待正确的ACK 或者等待ACK超时
                }
            } else if (tDevData.getCmd() == CEBC.CMD_APP_ACK_TYPE) {
                tDevData.setPriority(CEBC.PRIORITY_MIDDLE);
                push(tDevData);  // 再次push CEDevData 是为了发送 acK
                if (tDevData.getDataCrc16() == tDevData.figureCrc16())
                    proxyBase.receiptData(tDevData);
            }

        }
    }


    @Override
    public void dataSendSucceed(byte[] bytes) {
        Lg.e("接受到蓝牙数据发送成功的回掉 ：dataSendSucceed ");
        endSendDataTimeout();
        byteSendFailTimes = 0;
        CEDevData topDevData = peek();
        setSendState(sendFree);
        if (topDevData != null) {
            if (topDevData.getCmd() == CEBC.CMD_APP_ACK_TYPE) {
                // 一个接收数据成功的回复Ack包 不需要等待回ACK  直接发送下一个包
                CEDevData sendOverData = poll();
                topDevData = peek();
                sendDevData(topDevData);
            } else if (topDevData.getCurrentIndex() < topDevData.getTotalIndex()) {
                topDevData.setCurrentIndex(topDevData.getCurrentIndex() + 1);
                QueueSendData tSendData = protocol.getSendData(topDevData);
                currSendData = tSendData.getSendData();
                setSendState(sending);
                startSendDataTimeout();
                bluetooth.sendData(tSendData.index, tSendData.getSendData());
            } else {
                setSendState(sendwaitAck);
                sendTimeoutH.removeCallbacks(waitAckSendTimeoutR);
                sendTimeoutH.postDelayed(waitAckSendTimeoutR, timeOutTime);

            }
        }


    }
    @Override
    public synchronized void dataSendFailed(byte[] bytes) {
        endSendDataTimeout();
        dataSendFail(bytes);
    }
    private void dataSendFail(byte[] bytes){
        setSendState(sendFree);
        if (proxyBase.isConnectOK()) {
            if (byteSendFailTimes < maxSendFailTime) {
                byteSendFailTimes++;
                bluetooth.sendData(bytes);
                setSendState(sending);
            } else {
                CEDevData sendOverData = poll();
                byteSendFailTimes = 0;
                proxyBase.DataSendFailed(sendOverData);
                CEDevData topDevData = peek();
                sendDevData(topDevData);
            }
        } else {
            clearDate();
        }
    }

    private void ackEreOrWaitTimeOut() {
        // ack 错误 重新发送数据
        setSendState(sendFree);
        if (proxyBase.isConnectOK()) {
            if (dataSendFailTimes < maxSendFailTime) {
                dataSendFailTimes++;
                CEDevData topDevData = peek();
                if (topDevData == null) {
                    return;
                }
                topDevData.setCurrentIndex(0);
                sendDevData(topDevData);
            } else {
                dataSendFailTimes = 0;
                proxyBase.DataSendFailed(poll());
                CEDevData topDevData = peek();
                sendDevData(topDevData);
            }
        } else {
            clearDate();
        }
        Lg.e("数据发送失败  失败次数：" + dataSendFailTimes);
    }



    private void sendDevData(CEDevData topDevData) {
        if (topDevData != null) {
            setSendState(sending);
            topDevData.setPriority(CEBC.PRIORITY_CURR);
            QueueSendData tSendData = protocol.getSendData(topDevData);
            currSendData = tSendData.getSendData();
            startSendDataTimeout();
            bluetooth.sendData(tSendData.index, tSendData.getSendData());
        }
    }

    private synchronized void setSendState(int send) {
        sendState = send;
        Lg.e("蓝牙数据发送状态：" + sendState);
    }

    Handler sendTimeoutH = new Handler();

    private DataSendTimeoutR dataSendTimeoutR = new DataSendTimeoutR();
    private WaitAckSendTimeoutR waitAckSendTimeoutR = new WaitAckSendTimeoutR();

    class DataSendTimeoutR implements Runnable {
        @Override
        public void run() {
            dataSendFail(currSendData);
        }
    }

    class WaitAckSendTimeoutR implements Runnable {
        @Override
        public void run() {
            ackEreOrWaitTimeOut();
        }
    }

    private void addN() {
//        N++;
//        if (N > 255) {
//            N = 0;
//        }
    }

    private void startSendDataTimeout(){
        sendTimeoutH.removeCallbacks(dataSendTimeoutR);
        sendTimeoutH.postDelayed(dataSendTimeoutR,timeOutTime);
    }
    private void endSendDataTimeout(){
        sendTimeoutH.removeCallbacks(dataSendTimeoutR);
    }

}
